/*
 * Copyright (c) 2009-2010 Microelectronic Center, 
 * Harbin Institute of Technology
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gou Pengfei
 */

#ifndef __CPU_EDGE_ATOMIC_BASE_HH__
#define __CPU_EDGE_ATOMIC_BASE_HH__

#include <vector>
#include <set>

#include "base/statistics.hh"
#include "config/the_isa.hh"
#include "cpu/base.hh"
#include "cpu/simple_thread.hh"
#include "cpu/pc_event.hh"
#include "cpu/edge/static_inst.hh"
#include "cpu/edge/insttracer.hh"
#include "mem/packet.hh"
#include "mem/port.hh"
#include "mem/request.hh"
#include "sim/eventq.hh"
#include "sim/system.hh"

class Process;
class RemoteGDB;
class GDBListener;

namespace TheISA
{
    //class Predecoder;
}
class ThreadContext;
class Checkpoint;

namespace Trace {
    class EdgeInstRecord;
}

class AtomicEdgeCPUParams;

template<class Impl>
class BaseAtomicEdgeCPU : public BaseCPU
{
  public:
    typedef typename Impl::EdgeBlockPtr EdgeBlockPtr;
    typedef typename Impl::EdgeBlock EdgeBlock;
    typedef typename Impl::DynInstPtr DynInstPtr;
    typedef typename Impl::DynInst DynInst;

    typedef TheISA::HeaderInfo HeaderInfo;
    typedef TheISA::OpSize OpSize;
    typedef TheISA::BlockID BlockID;
    typedef TheISA::MachInst MachInst;

    typedef typename std::vector<DynInstPtr>::iterator InstQueueIt;
    typedef SimpleThread ImplState;

    /** 
      *  Structure to hold information of a lsq entry.
      */
    class LsqEntry 
    {
      public:
        LsqEntry()
        :valid(false),nullified(false),executed(false),
        store(false),instQueueIdx(-1),
        effAddr(0), accSize(0), fault(false)
        {}

        ~LsqEntry()
        {}

        /** Is this a valid entry? */
        bool valid;
        /** Has this entry been nullified? */
        bool nullified;
        /** Has this entry been executed? */
        bool executed;
        /** Is this a store entry? */
        bool store;
        
        /** Which instruction generate the result of this entry? */ 
        int instQueueIdx;

        /** 
         * Record the memory access address so that a
         * detailed model can get the memory dependent information by
         * reading this variable.
         * */
        Addr effAddr;

        /** 
         * Record the memory access size so that a
         * detailed model can get the memory dependent information by
         * reading this variable.
         * */
        int accSize;
        
        /** 
         * Record the fault status of this entry so that a
         * detailed model can get the memory dependent information by
         * reading this variable.
         * */
        bool fault;

        void reset()
        {
            valid = nullified = executed = store = false;
            instQueueIdx = -1;
            effAddr = 0;
            accSize = 0;
            fault = false;
        }
    };

  protected:
    //Trace::EdgeInstRecord *traceData;

    inline void checkPcEventQueue() {
        Addr oldpc;
        do {
            oldpc = thread->getBlockPC();
            system->pcEventQueue.service(tc);
        } while (oldpc != thread->getBlockPC());
    }

  public:
    void wakeup();

    void zero_fill_64(Addr addr) {
      static int warned = 0;
      if (!warned) {
        warn ("WH64 is not implemented");
        warned = 1;
      }
    };

  public:
    BaseAtomicEdgeCPU(AtomicEdgeCPUParams *params);
    virtual ~BaseAtomicEdgeCPU();

  public:
    /** SimpleThread object, provides all the architectural state. */
    SimpleThread *thread;

    /** ThreadContext object, provides an interface for external
     * objects to modify this thread's state.
     */
    ThreadContext *tc;
  protected:

    enum Status {
        Idle,
        Running,
        ITBWaitResponse,
        IcacheRetry,
        IcacheWaitResponse,
        IcacheWaitSwitch,
        DTBWaitResponse,
        DcacheRetry,
        DcacheWaitResponse,
        DcacheWaitSwitch,
        SwitchedOut
    };

    Status _status;

    /** Wake up consumers according to a completed inst. */
    int wakeDependences(DynInstPtr &completed_inst);

    /** Reset various queues for a new tick. */
    void resetQueues();
   
    Trace::EdgeInstTracer * edgeTracer;

  public:
    /// Provide access to the tracer pointer
    Trace::EdgeInstTracer * getEdgeTracer() { return edgeTracer; }

  public:

  protected:
   
    // A buffer holds all the bits of a inst block
    // fetched this tick. This is really a simple
    // buffer so no need to use STL implementation 
    // of containers.
    MachInst rawInstBuff[TheISA::MaxBlockSize][TheISA::ChunkSizeInWords];

    EdgeBlockPtr curEdgeBlockPtr;
    
    /**
      *  Instruction queue to hold all the insts
      *  to be executed.
      */
    std::vector<DynInstPtr> instQueue;

    /**
      *  List of ready instructions.
      */
    std::vector<int> readyList;

    /** 
      * List of compteted instructions belonged
      * to current pass.
      */
    std::vector<int> completedList;

    /** 
      *  List of instructions need to be replayed.
      */
    std::vector<int> replayList;

    /** 
      *  Load/Store queue to hold all the memory
      *   references to be executed. Note that there
      *  is no list for ready stores. This is because 
      *  stores should be written in lsid order thus 
      *  the ready list become useless.
      */
    std::vector<LsqEntry> ldstQueue;

    /**
      *  Read/write queues to hold all register operations.
      */
    std::vector<DynInstPtr> readQueue;
    std::vector<DynInstPtr> writeQueue;

    /** List of index of reads and writes to be written back.  */
    std::vector<int> readyReads;
    std::vector<int> readyWrites;

    /** LSID of the memory blocker. */
    TheISA::LsID memBlockerLSID;

    BlockID globalBlockID;
    InstSeqNum globalInstSeq;

    /** Size of the instruction size. */
    int instSize;

    /** Maximum passes allowed to execute one instruction block. */
    int maxPass;

    /** Max size of instruction queue. */
    int instQueueSize;
    
    /** Max size of ldstQueue. */
    int ldstQueueSize;

    /** Max size of read/write queues. */
    int readQueueSize;
    int writeQueueSize;

    /**
     * This is a bitset vector to record which instruction will get a
     * matched predication during the execution. It is used to
     * implement the perfect predication execution policy.
     *
     * False means the instruction in this position doesn't have valid
     * predication status or will get an unmatched predication in execution.
     *
     * True means the instruction in this position has a valid
     * predication status and will get a matched predication in
     * execution.
     * */
    std::bitset<TheISA::MaxInstsInBlock> predicationStatus;

    /** A container to record results of all TEST instructions for predication
     * path tracking. Each TEST inst corresponds to one bit in this container
     * in their inst ID order, i.e., testInstPath[0] is the first TEST inst in
     * one block. If one bit is set, means the corresponding TEST delivers a
     * TRUE value, vice versa.*/
    std::vector<bool> testInstPath;

  public:

    /** Set the predication status of an instruction. */
    void setPredicationStatus(int position)
    {
        assert(position < TheISA::MaxInstsInBlock);
        assert(instQueue[position]);
        assert(instQueue[position]->staticInst->getPredication()
               == TheISA::PredUponTrue ||
               instQueue[position]->staticInst->getPredication()
               == TheISA::PredUponFalse);

        predicationStatus.set(position);
    }

    /** Get the predication status of an instruction. */
    bool getPredicationStatus(int position) const
    { 
        assert(position < TheISA::MaxInstsInBlock);
        assert(instQueue[position]);
        assert(instQueue[position]->staticInst->getPredication()
               == TheISA::PredUponTrue ||
               instQueue[position]->staticInst->getPredication()
               == TheISA::PredUponFalse);

        return predicationStatus[position];
    }

    /** Set the predication status on a whole. */
    void setPredicationStatus(std::bitset<TheISA::MaxInstsInBlock> &pred_status) 
    {
        predicationStatus = pred_status;
    }

    /** Get the whole data structure holding the predication status.*/
    std::bitset<TheISA::MaxInstsInBlock> getPredicationStatus() const
    {
        return predicationStatus;
    }

    /** Get the test inst path. */
    std::vector<bool> getTestInstPath() const
    {
        return testInstPath;
    }

    #ifndef NDEBUG
    /** A counter for number of instructions this cpu created. */
    int instCount;
    int blockcount; // fix the naming style.
    #endif
    
    #ifdef DEBUG
    /** Debug structure to keep track of the sequence numbers still in
     * flight.
     */
    std::set<InstSeqNum> snList;
    #endif
    
    //This is the offset from the current pc that fetch should be performed at
    //Addr fetchOffset;
    //This flag says to stay at the current pc. This is useful for
    //instructions which go beyond MachInst boundaries.
    //bool stayAtPC;

    void updateMemBlockerLSID();

    void checkForInterrupts();
    void setupFetchRequest(Request *req);
    
    /** Consutruct EDGE header */
    void constructHeader(TheISA::MachInst *inst_chunk);
    
    void advancePC();

    virtual void deallocateContext(int thread_num);
    virtual void haltContext(int thread_num);

    // statistics
    virtual void regStats();
    virtual void resetStats();

    // number of simulated instructions
    Counter numInst;
    Counter startNumInst;
    Stats::Scalar numInsts;

    void countInst()
    {
        numInst++;
        numInsts++;

        thread->funcExeInst++;
    }

    virtual Counter totalInstructions() const
    {
        return numInst - startNumInst;
    }

    // Mask to align PCs to MachInst sized boundaries
    static const Addr PCMask = ~((Addr)sizeof(TheISA::MachInst) - 1);

    // number of simulated memory references
    Stats::Scalar numMemRefs;

    // number of simulated loads
    Counter numLoad;
    Counter startNumLoad;

    // number of idle cycles
    Stats::Average notIdleFraction;
    Stats::Formula idleFraction;

    // number of cycles stalled for I-cache responses
    Stats::Scalar icacheStallCycles;
    Counter lastIcacheStall;

    // number of cycles stalled for I-cache retries
    Stats::Scalar icacheRetryCycles;
    Counter lastIcacheRetry;

    // number of cycles stalled for D-cache responses
    Stats::Scalar dcacheStallCycles;
    Counter lastDcacheStall;

    // number of cycles stalled for D-cache retries
    Stats::Scalar dcacheRetryCycles;
    Counter lastDcacheRetry;

    virtual void serialize(std::ostream &os);
    virtual void unserialize(Checkpoint *cp, const std::string &section);

    void prefetch(Addr addr, unsigned flags)
    {
        // need to do this...
    }

    void writeHint(Addr addr, int size, unsigned flags)
    {
        // need to do this...
    }


    Fault copySrcTranslate(Addr src);

    Fault copy(Addr dest);

    // The register accessor methods provide the index of the
    // instruction's operand (e.g., 0 or 1), not the architectural
    // register index, to simplify the implementation of register
    // renaming.  We find the architectural register index by indexing
    // into the instruction's own operand index table.  Note that a
    // raw pointer to the StaticInst is provided instead of a
    // ref-counted StaticInstPtr to redice overhead.  This is fine as
    // long as these methods don't copy the pointer into any long-term
    // storage (which is pretty hard to imagine they would have reason
    // to do).

    uint64_t readIntRegOperand(const StaticInst *si, int idx)
    {
        return thread->readIntReg(si->srcRegIdx(idx));
    }

    void setIntRegOperand(const StaticInst *si, int idx, uint64_t val)
    {
        thread->setIntReg(si->destRegIdx(idx), val);
    }

    uint64_t readPC() { return thread->readPC(); }
    uint64_t readNextPC() { return thread->readNextPC(); }
    uint64_t readNextNPC() { return thread->readNextNPC(); }

    void setPC(uint64_t val) { thread->setPC(val); }
    void setNextPC(uint64_t val) { thread->setNextPC(val); }
    void setNextNPC(uint64_t val) { thread->setNextNPC(val); }

    void demapPage(Addr vaddr, uint64_t asn)
    {
        thread->demapPage(vaddr, asn);
    }

    void demapInstPage(Addr vaddr, uint64_t asn)
    {
        thread->demapInstPage(vaddr, asn);
    }

    void demapDataPage(Addr vaddr, uint64_t asn)
    {
        thread->demapDataPage(vaddr, asn);
    }

    unsigned readStCondFailures() {
        return thread->readStCondFailures();
    }

    void setStCondFailures(unsigned sc_failures) {
        thread->setStCondFailures(sc_failures);
    }

    //Fault CacheOp(uint8_t Op, Addr EA);
    void syscall(int64_t callnum) { thread->syscall(callnum); }

    bool misspeculating() { return thread->misspeculating(); }
    ThreadContext *tcBase() { return tc; }

    /** Update statistics related to inststruction fetching. */
    void updateInstStats(DynInstPtr &inst);

    /** Update statistics related to inst block fetching. */
    void updateInstBlockStats(EdgeBlockPtr &inst_block);

    /** Stat for total number of fetched instructions. */
    Stats::Scalar fetchedInsts;
    /** Stat for total number of fetched NOP insts. */
    Stats::Scalar fetchedNopInsts;
    /** Stat for total number of fetched NOP insts in header. */
    Stats::Scalar fetchedHeadNopInsts;
    /** Stat for total number of fetched MOV insts. */
    Stats::Scalar fetchedMovInsts;
    /** Stat for total number of fetched CONST insts. */
    Stats::Scalar fetchedConstInsts;
    /** Stat for total number of fetched TEST insts. */
    Stats::Scalar fetchedTestInsts;
    /** Stat for total number of fetched CONTROL insts. */
    Stats::Scalar fetchedControls;
    /** Stat for total number of fetched DIRECT CONTROL insts. */
    Stats::Scalar fetchedDirectControls;
    /** Stat for total number of fetched INDIRECT CONTROL insts. */
    Stats::Scalar fetchedIndirectControls;
    /** Stat for total number of fetched NULLIFY insts. */
    Stats::Scalar fetchedNullifies;
    /** Stat for total number of fetched MEMREF insts. */
    Stats::Scalar fetchedMemRefs;
    /** Stat for total number of fetched LOAD insts. */
    Stats::Scalar fetchedLoads;
    /** Stat for total number of fetched STORE insts. */
    Stats::Scalar fetchedStores;
    /** Stat for total number of fetched WRITE insts. */
    Stats::Scalar fetchedWrites;
    /** Stat for total number of fetched READ insts. */
    Stats::Scalar fetchedReads;
    /** Stat for toal number of fetched READ/WRITE insts. */
    Stats::Scalar fetchedReadWrites;
    /** Stat for total number of fetched insts with predication on true. */
    Stats::Scalar fetchedPredOnTrues;
    /** Stat for total number of fetched insts with predication on false. */
    Stats::Scalar fetchedPredOnFalses;

    /** Stat for total number of fetched NOP insts. */
    Stats::Distribution fetchedNopInstsRatio;
    /** Stat for total number of fetched NOP insts in header. */
    Stats::Distribution fetchedHeadNopInstsRatio;
    /** Stat for total number of fetched MOV insts. */
    Stats::Distribution fetchedMovInstsRatio;
    /** Stat for total number of fetched CONST insts. */
    Stats::Distribution fetchedConstInstsRatio;
    /** Stat for total number of fetched TEST insts. */
    Stats::Distribution fetchedTestInstsRatio;
    /** Stat for total number of fetched CONTROL insts. */
    Stats::Distribution fetchedControlsRatio;
    /** Stat for total number of fetched DIRECT CONTROL insts. */
    Stats::Distribution fetchedDirectControlsRatio;
    /** Stat for total number of fetched INDIRECT CONTROL insts. */
    Stats::Distribution fetchedIndirectControlsRatio;
    /** Stat for total number of fetched NULLIFY insts. */
    Stats::Distribution fetchedNullifiesRatio;
    /** Stat for total number of fetched MEMREF insts. */
    Stats::Distribution fetchedMemRefsRatio;
    /** Stat for total number of fetched LOAD insts. */
    Stats::Distribution fetchedLoadsRatio;
    /** Stat for total number of fetched STORE insts. */
    Stats::Distribution fetchedStoresRatio;
    /** Stat for total number of fetched WRITE insts. */
    Stats::Distribution fetchedWritesRatio;
    /** Stat for total number of fetched READ insts. */
    Stats::Distribution fetchedReadsRatio;
    /** Stat for toal number of fetched READ/WRITE insts. */
    Stats::Distribution fetchedReadWritesRatio;
    /** Stat for total number of fetched insts with predication on true. */
    Stats::Distribution fetchedPredOnTruesRatio;
    /** Stat for total number of fetched insts with predication on false. */
    Stats::Distribution fetchedPredOnFalsesRatio;
    
    /** Stat for distribution of number of operands of fetched insts. */
    Stats::Distribution fetchedNumOperands;
    /** Stat for ditribution of number of consumers of fetched insts. */
    Stats::Distribution fetchedNumConsumers;
    /** Stat for distribution of size of fetched inst blocks. */
    Stats::Distribution fetchedInstBlockSize;

    /** Stat for total number of fetched blocks. */
    Stats::Scalar fetchedBlocks;
    /** Stat for total number of fetched instruction chunks. */
    Stats::Scalar fetchedChunks;

    Stats::Scalar iqPredicationPropagated;
    Stats::Scalar iqOperandsPropagated;
    Stats::Scalar iqNullTokenPropagated;
    Stats::Scalar iqExceptTokenPropagated;
};

#endif // __CPU_EDGE_ATOMIC_BASE_HH__
